#include "os_uart.h"
#include <stdio.h>
#include "stm32f10x.h"
#include <string.h>

static uint8_t port_used = 0;

static bool overflow = 0;

os_uart_device_t uart_devices[OS_UART_COUNT];

/* atos_uart_receive_callback uart fd always correct */
void atos_uart_receive_callback(os_uart_fd_t fd, uint8_t data)
{
	if (NULL != fd->fifo.buf)
	{
		//if (false == atos_fifo_push(&fd->fifo, &data, 1))
		if (false == atos_fifo_push_byte(&fd->fifo, data))
		{
			if (!overflow)
			{
				printf("uart overflow 0x%x: `%c` 0x%02x\r\n", fd->fd, data, data);
				overflow = true;
			}
			//osa_trace_direct("TODO:FIXME: data dropped\n");
		}
		else
		{
			overflow = false;
		}
	}
	if (NULL != fd->callback)
	{
		fd->callback(data);
	}
}

void USART1_IRQHandler(void)
{
	while (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		atos_uart_receive_callback(uart_devices + 0, USART_ReceiveData(USART1));
	}
}

void USART2_IRQHandler(void)
{
	while (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
	{
		atos_uart_receive_callback(uart_devices + 1, USART_ReceiveData(USART2));
	}
}

void USART3_IRQHandler(void)
{
	while (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
	{
		atos_uart_receive_callback(uart_devices + 2, USART_ReceiveData(USART3));
	}
}

void UART4_IRQHandler(void)
{
	while (USART_GetITStatus(UART4, USART_IT_RXNE) != RESET)
	{
		atos_uart_receive_callback(uart_devices + 3, USART_ReceiveData(UART4));
	}
}


void UART5_IRQHandler(void)
{
	while (USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)
	{
		atos_uart_receive_callback(uart_devices + 4, USART_ReceiveData(UART5));
	}
}


void uart0_init(uint32_t baud_rate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/* Enable GPIO clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	
	/* Enable UART clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	
	/* Configure USART Tx as alternate function push-pull */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* Configure USART Rx as input floating */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* USART configuration */
	USART_InitStructure.USART_BaudRate = baud_rate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART1, &USART_InitStructure);
	
	/* Enable USART */
	USART_Cmd(USART1, ENABLE);

	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
}

void uart1_init(uint32_t baud_rate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/* Enable GPIO clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	
	/* Enable UART clock */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	
	/* Configure USART Tx as alternate function push-pull */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* Configure USART Rx as input floating */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/* USART configuration */
	USART_InitStructure.USART_BaudRate = baud_rate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART2, &USART_InitStructure);
	
	/* Enable USART */
	USART_Cmd(USART2, ENABLE);
	
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	
}

void uart2_init(uint32_t baud_rate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/* Enable GPIO clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
	
	/* Enable UART clock */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	
	
	/* Configure USART Tx as alternate function push-pull */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	/* Configure USART Rx as input floating */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	/* USART configuration */
	USART_InitStructure.USART_BaudRate = baud_rate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USART3, &USART_InitStructure);
	
	/* Enable USART */
	USART_Cmd(USART3, ENABLE);

	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
}

void uart3_init(uint32_t baud_rate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/* Enable GPIO clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
	
	/* Enable UART clock */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
	
	
	/* Configure USART Tx as alternate function push-pull */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	/* Configure USART Rx as input floating */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	/* USART configuration */
	USART_InitStructure.USART_BaudRate = baud_rate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(UART4, &USART_InitStructure);

	NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	
	/* Enable USART */
	USART_Cmd(UART4, ENABLE);
	
	USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);
}

void uart4_init(uint32_t baud_rate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/* Enable GPIO clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
	
	/* Enable UART clock */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
	
	
	/* Configure USART Tx as alternate function push-pull */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);   //PC12  -->  UART5_TX
	
	/* Configure USART Rx as input floating */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_Init(GPIOD, &GPIO_InitStructure);   //PD2  -->   UART5_RX
	
	/* USART configuration */
	USART_InitStructure.USART_BaudRate = baud_rate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(UART5, &USART_InitStructure);

	NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	
	/* Enable USART */
	USART_Cmd(UART5, ENABLE);
	
	USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);
}


void os_uart_init()
{
	memset(uart_devices, 0, sizeof(uart_devices));
}

os_uart_fd_t ucos_uart_get_fd(
        const char *port_name,
        os_uart_rec_callback_t callback,
        uint8_t *buf,
        uint16_t size)
{
	os_uart_fd_t fd = OS_UART_INVALID_FD;
	char port;
	if (NULL == port_name)
	{
		return fd;
	}
	port = *port_name;
	if (port < '1')
	{
		return fd;
	}
	port -= '1';
	if (port >= OS_UART_COUNT)
	{
		return fd;
	}
	fd = uart_devices + port;
	fd->fd = port;
	fd->callback = callback;
	fd->sem = NULL;
	atos_fifo_create(&fd->fifo, buf, size);
	return fd;
}

/*
  * @brief: init uart
  * @param: device config
  * @ret:
  *    0:  OK
  *    1: used
  *    2: error port
  */

DLL_API os_uart_fd_t os_uart_open(
        const char * port_name,
        uint32_t baud_rate,
        os_uart_rec_callback_t callback,
        uint8_t *buf,
        uint16_t size)
{
	os_uart_fd_t fd = ucos_uart_get_fd(port_name, callback, buf, size);
	if (fd == OS_UART_INVALID_FD)
	{
		return fd;
	}
	switch(fd->fd)
	{
		case 0:
			if(port_used &(1 << 0))
				return OS_UART_INVALID_FD;
			else
			{
				port_used |= 1 << 0;
				uart0_init(baud_rate);
			}
			break;
		case 1:
			if(port_used &(1 << 1))
				return OS_UART_INVALID_FD;
			else
			{
				port_used |= 1 << 1;
				uart1_init(baud_rate);
			}
			break;
		case 2:
			if(port_used & (1 << 2))
				return OS_UART_INVALID_FD;
			else
			{
				port_used |= 1 << 2;
				uart2_init(baud_rate);
			}
			break;
		case 3:
			if(port_used & (1 << 3))
				return OS_UART_INVALID_FD;
			else
			{
				port_used |= 1 << 3;
				uart3_init(baud_rate);
			}
			break;
		case 4:
			if(port_used & (1 << 4))
				return OS_UART_INVALID_FD;
			else
			{
				port_used |= 1 << 4;
				uart4_init(baud_rate);
			}
			break;
		default:
			break;
	}
	return fd;
}

DLL_API osa_result_t os_uart_close(os_uart_fd_t fd)
{
	uint8_t event_err;
	if (fd == OS_UART_INVALID_FD)
	{
		return OSA_FAIL;
	}
	if (fd->sem != NULL)
	{
		OSSemDel(fd->sem, OS_DEL_NO_PEND, &event_err);
	}
	fd->fifo.buf = NULL;
	port_used &= ~(1 << fd->fd);
	return OSA_SUCCESS;
}

uint16_t os_uart_receive_byte(
        os_uart_fd_t fd,
        uint8_t *data)
{
	if (fd == OS_UART_INVALID_FD)
	{
		return 0;
	}
	return atos_fifo_pop_byte(&fd->fifo, data);
}

uint16_t os_uart_receive_data(
        os_uart_fd_t fd,
        void *buf,
        uint16_t len)
{
	if (fd == OS_UART_INVALID_FD)
	{
		return 0;
	}
	return atos_fifo_pop(&fd->fifo, buf, len);
}

uint16_t os_uart_send_data(
        os_uart_fd_t fd,
        const void *buf,
        uint16_t len)
{
	uint16_t i = 0;
	if (fd == OS_UART_INVALID_FD)
	{
		return 0;
	}
	for (i = 0; i < len; i++)
	{
		os_uart_send_byte(fd, ((uint8_t*) buf)[i]);
	}
	return len;
}

uint16_t os_uart_send_byte(os_uart_fd_t fd, uint8_t byte)
{
	if (fd == OS_UART_INVALID_FD)
	{
		return 0;
	}
	switch (fd->fd)
	{
	case 0:
		/* Loop until the end of transmission : Speed up.*/
		while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
		{
		}

		USART_SendData(USART1, byte);
		break;
	case 1:
		/* Loop until the end of transmission : Speed up.*/
		while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
		{
		}

		USART_SendData(USART2, byte);
		break;
	case 2:
		/* Loop until the end of transmission : Speed up.*/
		while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET)
		{
		}

		USART_SendData(USART3, byte);
		break;
	case 3:
		/* Loop until the end of transmission : Speed up.*/
		while (USART_GetFlagStatus(UART4, USART_FLAG_TC) == RESET)
		{
		}

		USART_SendData(UART4, byte);
		break;
	case 4:
		/* Loop until the end of transmission : Speed up.*/
		while (USART_GetFlagStatus(UART5, USART_FLAG_TC) == RESET)
		{
		}

		USART_SendData(UART5, byte);
		break;
	default:
		break;
	}

	return 1;
}
